/** Host interface for html5 web browsers.
*/
var host = {
	/** The width of a character in pixels. */
	_cellWidth: 16,
	/** The height of a character in pixels. */
	_cellHeight: 16,
	/** Font family. */
	_fontFamily: "sans-serif",
	/** Font weight. */
	_fontWeight: "bold",
	/** The DOM Node of the base screen div. */
	_screenDiv: null,
	/** Array of screen cell div DOM nodes. */
	_cells: null,
	/** Width of the screen in cells. */
	_width: 0,
	/** Height of the screen in cells. */
	_height: 0,
	/** Input handling flag. */
	_nonChar: false,
	/** Input action consumer method. */
	inputConsumer: null,
	/** The screen buffer. */
	screen: null,
	/** Initialize the input and display.
	@param width Width of the display in cells.
	@param height Height of the display in cells.
	@param inputConsumer Input consumer (may be null).
	*/
	init: function(width, height, inputConsumer)
	{
		// TODO Implement Chrome Frame support
		this.inputConsumer = inputConsumer;
		this._setUpDisplay(width, height);
		document.onkeydown = function(e)
		{
			return host.keyDown(e);
		}
		document.onkeypress = function(e)
		{
			return host.keyDown(e);
		}

		// Special "active" screen buffer object
		function ActiveScreenBuffer() {}
		ActiveScreenBuffer.prototype = new ScreenBuffer(width, height);
		ActiveScreenBuffer.prototype.setBufferCell = ActiveScreenBuffer.prototype.setCell;
		ActiveScreenBuffer.prototype.setCell = this._setScreenCell;
		this.screen = new ActiveScreenBuffer();
		this.screen.width = width;
		this.screen.height = height;
	},
	/** Keyboard input handling.
	@param e The event object.
	*/
	keyDown: function(e)
	{
		var c, action = "none";
		var evt = (e) ? e : window.event;
		
		if(evt.type == "keydown")
		{
			c = evt.keyCode;
			if(c <16 ||
				(c> 16 && c <32) ||
				(c> 32 && c <41) ||
				c == 46)
			{
				this._nonChar = true;
				
				// Handle non-character keys
				switch(c)
				{
					case 13: action = "use"; break;
					case 27: action = "cancel"; break;
					case 33: action = "logup"; break;
					case 34: action = "logdown"; break;
					case 35: action = "historydown"; break;
					case 36: action = "historyup"; break;
					case 37: action = "left"; break;
					case 38: action = "up"; break;
					case 39: action = "right"; break;
					case 40: action = "down"; break;
					default:
						//alert(c);
						break;
				}
				if(action != "none")
					this.dispatchInputAction(action);

				try
				{
					if(Event.stop)
						Event.stop(evt);
				}
				catch(exp){}
				evt.returnValue = false;
			}
			else
				this._nonChar = false;
		}
		else
		{
			if(this._nonChar)
				return true;
			c = (evt.charCode) ? evt.charCode : evt.keyCode;
			if(c> 31 && c < 256)
			{
				switch(c)
				{
					case 32: action = "menu"; break;
					case 43: action = "cancel"; break;
					case 47: action = "help"; break;
					case 48: action = "menu"; break;
					case 50: action = "down"; break;
					case 51: action = "look"; break;
					case 52: action = "left"; break;
					case 54: action = "right"; break;
					case 55: action = "inventory"; break;
					case 56: action = "up"; break;
					case 63: action = "help"; break;
					case 104: action = "help"; break;
					case 105: action = "inventory"; break;
					case 108: action = "look"; break;
					default:
						//alert(c);
						break;
				}
				if(action != "none")
					this.dispatchInputAction(action);

				try
				{
					if(Event.stop)
						Event.stop(evt);
				}
				catch(exp){}
				evt.returnValue = false;
			}
		}
		return evt.returnValue;
	},
	/** Dispatches a single input action.
	@param action Name of the action.
	*/
	dispatchInputAction: function(action)
	{
		if(this.inputConsumer !== null)
			this.inputConsumer(action);
	},
	/** Test is saving is available.
	 */
	isSaveAvailable: function()
	{
		if(window.localStorage)
			return true;
		return false;
	},
	/** Test if a value is available.
	 *@param key THe key name of the value.
	 */
	isValueAvailable: function(key)
	{
		if(!window.localStorage)
			throw new Error("Save feature is not available in this browser.");
		if(window.localStorage[key] !== undefined &&
			window.localStorage[key].length > 0)
			return true;
		return false;
	},
	/** Save a key-value pair.
	 * @param key The key name.
	 * @param value The value to store.
	 */
	saveData: function(key, value)
	{
		if(!window.localStorage)
			throw new Error("Save feature is not available in this browser.");
		window.localStorage[key] = value;
	},
	/** Loads a key-value pair.
	 *@param key The key name.
	 */
	loadData: function(key)
	{
		if(!window.localStorage)
			throw new Error("Save feature is not available in this browser.");
		return window.localStorage[key];
	},
	/** Delete a key-value pair.
	 *@param key The key name.
	 */
	deleteData: function(key)
	{
		if(!window.localStorage)
			throw new Error("Save feature is not available in this browser.");
		window.localStorage[key] = "";
		delete(window.localStorage[key]);
	},
	/** Set a screen cell.
	@param x X position.
	@param y Y position.
	@param c Character to put into the cell.
	@param bg Background color of the cell.
	@param fg Foreground color of the cell.
	*/
	_setScreenCell: function(x, y, c, fg, bg)
	{
		this.setBufferCell(x, y, c, fg, bg);
		if(x < 0 ||
			x >= host._width ||
			y < 0 ||
			y >= host._height)
			return;
		var cellDiv = host._cells[y * host._width + x];
		if(bg !== null)
			cellDiv.style.backgroundColor = bg.cssColorString();
		if(fg !== null)
			cellDiv.style.color = fg.cssColorString();
		if(c !== null)
			// Fix for IE
			cellDiv.firstChild.innerHTML = c == "<" ? "&lt;" : c;
	},
	/** (re)Initialize the display.
	@param width Width of the display in cells.
	@param height Height of the display in cells.
	*/
	_setUpDisplay: function(width, height)
	{
		this._removeChildren(document.body);
		this._width = width;
		this._height = height;
		this._screenDiv = document.createElement("div");
		this._screenDiv.style.width = (width * this._cellWidth) + "px";
		this._screenDiv.style.height = (height * this._cellHeight) + "px";
		this._screenDiv.style.position = "relative";
		this._screenDiv.style.padding = "0";
		this._screenDiv.style.margin = "0";
		this._screenDiv.style.fontFamily = this._fontFamily;
		this._screenDiv.style.fontSize = this._cellHeight + "px";
		this._screenDiv.style.border = "none";
		this._cells = [];
		for(var y = 0; y < height; ++y)
		{
			for(var x = 0; x < width; ++x)
			{
				var cellDiv = document.createElement("div");
				cellDiv.style.width = (this._cellWidth + 1) + "px";
				cellDiv.style.height = (this._cellHeight + 1) + "px";
				cellDiv.style.textAlign = "center";
				cellDiv.style.backgroundColor = "#000000";
				cellDiv.style.position = "absolute";
				cellDiv.style.top = (y * this._cellHeight) + "px";
				cellDiv.style.left = (x * this._cellWidth) + "px";
				cellDiv.style.padding = "0";
				cellDiv.style.margin = "0";
				cellDiv.style.border = "none";
				var inner = document.createElement("div");
				inner.style.position = "absolute";
				inner.style.top = "-2px";
				inner.style.left = "0px";
				inner.style.padding = "0";
				inner.style.margin = "0";
				inner.style.textAlign = "center";
				inner.style.width = this._cellWidth + "px";
				inner.style.height = this._cellHeight + "px";
				inner.style.border = "none";
				inner.style.fontWeight = this._fontWeight;
				cellDiv.appendChild(inner);
				this._cells[y * this._width + x] = cellDiv;
				this._screenDiv.appendChild(cellDiv);
			}
		}
		document.body.appendChild(this._screenDiv);
	},
	/** Remove all children from a DOM Node.
	@param node Node to remove children from.
	*/
	_removeChildren: function(node)
	{
		while(node.hasChildNodes())
		{
			node.removeChild(node.lastChild);
		}
	}
};
